package bdz.projects.inl.model {
	
	import bdz.common.events.XMLLoaderEvent;
	import bdz.common.model.XMLLoader;
	import bdz.projects.inl.core.CoreVO;
	import bdz.projects.inl.events.INLModelEvent;
	import bdz.projects.inl.vo.*;
	
	import flash.events.EventDispatcher;
	import flash.net.FileReference;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	public class INLModel extends EventDispatcher {
		
		private static var instance:INLModel;
		private static var baseURL:String;
		private static var _products:ProductCollection;
		private static var _projects:ProjectCollection;
		private static var _users:UserCollection;
		private static var _downloads:DownloadCollection;
		private static var _news:NewsCollection;
		private static var _home:HomeCollection;
		private static var _autoTypes:Array;
		private static var _linkCombination:Dictionary;
		public function INLModel(se:SingletonEnforcer) {
			super();
		}
		public static function getInstance(burl:String = null):INLModel{
			if(instance == null){
				instance = new INLModel(new SingletonEnforcer());
				instance.initializeComponent(burl);	
				
			}
			return instance;
		}
		private function initializeComponent(burl:String):void{
			baseURL = burl;
			_autoTypes = new Array();
			_autoTypes.push({typeClass:HomeVO, insert:insertHome, update:updateHome, remove:deleteHome});
			_autoTypes.push({typeClass:ProductVO, insert:insertProduct, update:updateProduct, remove:deleteProduct});
			_autoTypes.push({typeClass:ProjectVO, insert:insertProject, update:updateProject, remove:deleteProject});
			_autoTypes.push({typeClass:NewsVO, insert:insertNews, update:updateNews, remove:deleteNews});
			_autoTypes.push({typeClass:UserVO, insert:insertUser, update:updateUser, remove:deleteUser});
			_autoTypes.push({typeClass:DownloadVO, insert:insertDownload, update:updateDownload, remove:deleteDownload});
			
			_autoTypes.push({typeClass:ProductManufacturerVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PRODUCT_MANUFACTURER, updatemethod:INLMethods.UPDATE_PRODUCT_MANUFACTURER, removemethod:INLMethods.DELETE_PRODUCT_MANUFACTURER,
							insertobj:ProductManufacturerVO, updateobj:ProductManufacturerVO, removeobj:ProductManufacturerCollection});
			_autoTypes.push({typeClass:ProductControlGearVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PRODUCT_CONTROLGEARS, updatemethod:INLMethods.UPDATE_PRODUCT_CONTROLGEARS, removemethod:INLMethods.DELETE_PRODUCT_CONTROLGEARS,
							insertobj:ProductControlGearVO, updateobj:ProductControlGearVO, removeobj:ProductControlGearCollection});
			_autoTypes.push({typeClass:ProductLightSourceVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PRODUCT_LIGHTSOURCE, updatemethod:INLMethods.UPDATE_PRODUCT_LIGHTSOURCE, removemethod:INLMethods.DELETE_PRODUCT_LIGHTSOURCE,
							insertobj:ProductLightSourceVO, updateobj:ProductLightSourceVO, removeobj:ProductLightSourceCollection});
			_autoTypes.push({typeClass:ProductTypeVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PRODUCT_TYPE, updatemethod:INLMethods.UPDATE_PRODUCT_TYPE, removemethod:INLMethods.DELETE_PRODUCT_TYPE,
							insertobj:ProductTypeVO, updateobj:ProductTypeVO, removeobj:ProductTypeCollection});
			_autoTypes.push({typeClass:ProductFinishVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PRODUCT_FINISH, updatemethod:INLMethods.UPDATE_PRODUCT_FINISH, removemethod:INLMethods.DELETE_PRODUCT_FINISH,
							insertobj:ProductFinishVO, updateobj:ProductFinishVO, removeobj:ProductFinishCollection});
							
			_autoTypes.push({typeClass:ProjectCategoryVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PROJECT_CATEGORY, updatemethod:INLMethods.UPDATE_PROJECT_CATEGORY, removemethod:INLMethods.DELETE_PROJECT_CATEGORY,
							insertobj:ProjectCategoryVO, updateobj:ProjectCategoryVO, removeobj:ProjectCategoryCollection});
			_autoTypes.push({typeClass:ProjectCreditVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_PROJECT_CREDIT, updatemethod:INLMethods.UPDATE_PROJECT_CREDIT, removemethod:INLMethods.DELETE_PROJECT_CREDIT,
							insertobj:ProjectCreditVO, updateobj:ProjectCreditVO, removeobj:ProjectCreditCollection});
							
			_autoTypes.push({typeClass:DownloadFolderVO, insert:insertDependency, update:updateDependency, remove:deleteDependency, 
							insertmethod:INLMethods.INSERT_DOWNLOAD_FOLDER, updatemethod:INLMethods.UPDATE_DOWNLOAD_FOLDER, removemethod:INLMethods.DELETE_DOWNLOAD_FOLDER,
							insertobj:DownloadFolderVO, updateobj:DownloadFolderVO, removeobj:DownloadFolderCollection});
							
			//need to add 8 more here
			
			
			_linkCombination = new Dictionary();
			_linkCombination["homevo_productvo"] = {remove:INLMethods.DELETE_HOME_PRODUCT_LINK};
			_linkCombination["homevo_projectvo"] = {remove:INLMethods.DELETE_HOME_PROJECT_LINK};
			
			_linkCombination["newsvo_newsmediavo"] = {remove:INLMethods.DELETE_NEWS_MEDIA_LINK};
			_linkCombination["newsvo_projectvo"] = {remove:INLMethods.DELETE_NEWS_PROJECT_LINK};
			_linkCombination["newsvo_productvo"] = {remove:INLMethods.DELETE_NEWS_PRODUCT_LINK};
			
			
			_linkCombination["productvo_productmediavo"] = {remove:INLMethods.DELETE_PRODUCT_MEDIA_LINK};
			_linkCombination["productvo_productmanufacturervo"] = {remove:INLMethods.DELETE_PRODUCT_MANUFACTURER_LINK};
			_linkCombination["productvo_producttypevo"] = {remove:INLMethods.DELETE_PRODUCT_TYPE_LINK};
			_linkCombination["productvo_productfinishvo"] = {remove:INLMethods.DELETE_PRODUCT_FINISH_LINK};
			_linkCombination["productvo_productcontrolgearvo"] = {remove:INLMethods.DELETE_PRODUCT_CONTROLGEAR_LINK};
			_linkCombination["productvo_productlightsourcevo"] = {remove:INLMethods.DELETE_PRODUCT_LIGHTSOURCE_LINK};
			
			_linkCombination["projectvo_projectmediavo"] = {remove:INLMethods.DELETE_PROJECT_MEDIA_LINK};
			_linkCombination["projectvo_projectcategoryvo"] = {remove:INLMethods.DELETE_PROJECT_CATEGORY_LINK};
			_linkCombination["projectvo_projectcreditvo"] = {remove:INLMethods.DELETE_PROJECT_CREDIT_LINK};
			_linkCombination["projectvo_productvo"] = {remove:INLMethods.DELETE_PROJECT_PRODUCT_LINK};
			
			_linkCombination["uservo_downloadvo"] = {remove:INLMethods.DELETE_USER_DOWNLOAD_LINK};
			_linkCombination["downloadvo_downloadfoldervo"] = {remove:INLMethods.DELETE_DOWNLOAD_FOLDER_LINK};
			
		}
		private function hasError(xm:XML):Boolean{
			if(xm){
				return (xm.name() == "inl_error");
			}
			return true;
		}
		private function onDataFault(evt:XMLLoaderEvent):void{
			dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_ERROR, evt.data));
		}
		private function processUpload(responder:Function, vars:Object, f:FileReference, fault:Function = null, type:String = ""):void{
			var callback:Function = function(evt:XMLLoaderEvent):void{
				try{
					var result:XML = new XML(evt.data);	
				}catch(e:Error){}
				
				if(!hasError(result)){
					responder(result);
				}else{
					if(fault != null){
						fault(result);
					}else{
						dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_ERROR, result));
					}
				}
				dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_COMPLETE, type));
			}
			var xloader:XMLLoader = new XMLLoader("onCallback");
			xloader.location = baseURL;
			xloader.addEventListener("onCallback", callback);
			xloader.addEventListener(XMLLoaderEvent.FAULT, onDataFault);
			xloader.sendPostHelp(vars, f.name, f.data, true);
			dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_STARTED, type));
		}
		private function process(responder:Function, proc:XML, obj:Object = null, fault:Function = null, type:String = ""):void{
			var callback:Function = function(evt:XMLLoaderEvent):void{
				
				try{
					var result:XML = new XML(evt.data);
					
				}catch(e:Error){}
				
				if(!hasError(result)){
					if(obj){
						responder(new obj(result));
					}else{
						responder(result);
					}
					
				}else{
					
					if(fault != null){
						fault(result);
					}else{
						dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_ERROR, result));
					}
				}
				
				xloader.removeEventListener("onCallback", callback);
				callback = null;
				dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_COMPLETE, type));
			}
			
			var xloader:XMLLoader = new XMLLoader("onCallback");
			xloader.location = baseURL;
			xloader.addEventListener("onCallback", callback);
			xloader.addEventListener(XMLLoaderEvent.FAULT, onDataFault);
			xloader.sendCompressed(proc);
			
			dispatchEvent(new INLModelEvent(INLModelEvent.REQUEST_STARTED, type));
		}
		public function login(username:String, password:String, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.LOGIN;
			xm.username = new XML("<username>"+username+"</username>");
			xm.password = new XML("<password>"+password+"</password>");
			
			process(responder, xm, UserVO, fault);
		}
		public function getNews(responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_NEWS;
			process(responder, xm, NewsCollection, fault);
		}
		public function insertNews(nvo:NewsVO, responder:Function, fault:Function = null):void{
			var serial:XML = nvo.serialized;
			serial.@method = INLMethods.INSERT_NEWS;
			process(responder, serial, NewsVO, fault);
		}
		public function updateNews(nvo:NewsVO, responder:Function, fault:Function = null):void{
			var serial:XML = nvo.serialized;
			serial.@method = INLMethods.UPDATE_NEWS;
			
			process(responder, serial, NewsVO, fault);
		}
		public function deleteNews(nvo:NewsVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_NEWS;
			xm.id = new XML("<id>"+nvo.news_id+"</id>");
			process(responder, xm, NewsCollection, fault);
		}
		public function getProjects(responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_PROJECTS;
			process(responder, xm, ProjectCollection, fault);
		}
		public function insertProject(pvo:ProjectVO, responder:Function, fault:Function = null):void{
			var serial:XML = pvo.serialized;
			serial.@method = INLMethods.INSERT_PROJECT;
			process(responder, serial, ProjectVO, fault);
		}
		public function updateProject(pvo:ProjectVO, responder:Function, fault:Function = null):void{
			var serial:XML = pvo.serialized;
			serial.@method = INLMethods.UPDATE_PROJECT;
			process(responder, serial, ProjectVO, fault);
		}
		public function deleteProject(pvo:ProjectVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_PROJECT;
			xm.id = new XML("<id>"+pvo.project_id+"</id>");
			process(responder, xm, ProjectCollection, fault);
		}
		public function getProducts(responder:Function, fault:Function = null, info:Object = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_PRODUCTS;
			if(info){
				xm.@start = info.startIndex;
				xm.@end = info.length;
			}
			process(responder, xm, ProductCollection, fault);
		}
		public function insertProduct(pvo:ProductVO, responder:Function, fault:Function = null):void{
			var serial:XML = pvo.serialized;
			
			serial.@method = INLMethods.INSERT_PRODUCT;
			
			process(responder, serial, ProductVO, fault);
		} 
		
		public function updateProduct(pvo:ProductVO, responder:Function, fault:Function = null):void{
			var serial:XML = pvo.serialized;
			serial.@method = INLMethods.UPDATE_PRODUCT;
			process(responder, serial, ProductVO, fault);
			
		}
		public function deleteProduct(pvo:ProductVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_PRODUCT;
			xm.id = new XML("<id>"+pvo.product_id+"</id>");
			process(responder, xm, ProductCollection, fault);
		}
		public function getHome(responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_HOME;
			process(responder, xm, HomeCollection, fault);
		}
		public function insertHome(hvo:HomeVO, responder:Function, fault:Function = null):void{
			var serial:XML = hvo.serialized;
			serial.@method = INLMethods.INSERT_HOME;
			process(responder, serial, HomeVO);
		}
		public function updateHome(hvo:HomeVO, responder:Function, fault:Function = null):void{
			var serial:XML = hvo.serialized;
			serial.@method = INLMethods.UPDATE_HOME;
			process(responder, serial, HomeVO, fault);
		}
		public function deleteHome(hvo:HomeVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_HOME;
			xm.id = new XML("<id>"+hvo.home_id+"</id>");
			
			process(responder, xm, HomeCollection, fault);
		}
		public function getUsers(responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_USERS;
			process(responder, xm, UserCollection, fault);
		}
		public function insertUser(uvo:UserVO, responder:Function, fault:Function = null):void{
			var serial:XML = uvo.serialized;
			serial.@method = INLMethods.INSERT_USER;
			process(responder, serial, UserVO, fault);
		}
		public function updateUser(uvo:UserVO, responder:Function, fault:Function = null):void{
			var serial:XML = uvo.serialized;
			serial.@method = INLMethods.UPDATE_USER;
			process(responder, serial, UserVO, fault);
		}
		public function deleteUser(uvo:UserVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_USER;
			xm.id = new XML("<id>"+uvo.user_id+"</id>");
			process(responder, xm, UserCollection, fault);
		}
		public function getDownloads(responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.GET_DOWNLOADS;
			process(responder, xm, DownloadCollection, fault);
		}
		public function insertDownload(dvo:DownloadVO, responder:Function, fault:Function = null):void{
			var serial:XML = dvo.serialized;
			serial.@method = INLMethods.INSERT_DOWNLOAD;
			
			process(responder, serial, DownloadVO, fault);
		}
		public function updateDownload(dvo:DownloadVO, responder:Function, fault:Function = null):void{
			var serial:XML = dvo.serialized;
			serial.@method = INLMethods.UPDATE_DOWNLOAD;
			
			process(responder, serial, DownloadVO, fault);
		}
		public function deleteDownload(dvo:DownloadVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = INLMethods.DELETE_DOWNLOAD;
			xm.id = new XML("<id>"+dvo.download_id+"</id>");
			process(responder, xm, DownloadCollection, fault);
		}
		public function getDependency(method:String, responder:Function, dobj:Object, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = method;
			process(responder, xm, dobj, fault);
		}
		public function insertDependency(method:String, cvo:CoreVO, responder:Function, dobj:Object, fault:Function = null):void{
			var serial:XML = cvo.serialized;
			serial.@method = method;
			process(responder, serial, dobj, fault);
		}
		public function updateDependency(method:String, cvo:CoreVO, responder:Function, dobj:Object, fault:Function = null):void{
			var serial:XML = cvo.serialized;
			serial.@method = method;
			trace(serial.toXMLString());
			process(responder, serial, dobj, fault);
		}
		public function deleteDependency(method:String, cvo:CoreVO, responder:Function, dobj:Object, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			xm.@method = method;
			xm.id = new XML("<id>"+cvo[cvo.idField]+"</id>");
			process(responder, xm, dobj, fault);
		}
		public function uploadImage(fr:FileReference, responder:Function, fault:Function = null):void{
			processUpload(responder, {method:INLMethods.UPLOAD_IMAGE}, fr, fault);
		}
		public function uploadFile(fr:FileReference, responder:Function, fault:Function = null):void{
			processUpload(responder, {method:INLMethods.UPLOAD_FILE}, fr, fault);							
		}
		public function autoUpdateVO(vo:CoreVO, responder:Function, fault:Function = null):void{
			
			for each(var obj:Object in _autoTypes){
				if(vo is obj.typeClass){
					if(obj.updatemethod == null){
						obj.update(vo, responder, fault);
					}else{
						obj.update(obj.updatemethod, vo, responder, obj.updateobj, fault);
					}
				}
			}
		}
		public function autoInsertVO(vo:CoreVO, responder:Function, fault:Function = null):void{
			for each(var obj:Object in _autoTypes){
				if(vo is obj.typeClass){
					if(obj.insertmethod == null){
						obj.insert(vo, responder, fault);
					}else{
						obj.insert(obj.insertmethod, vo, responder, obj.insertobj, fault);
					}
				}
			}
		}
		public function autoDeleteVO(vo:CoreVO, responder:Function, fault:Function = null):void{
			for each(var obj:Object in _autoTypes){
				if(vo is obj.typeClass){
					if(obj.removemethod == null){
						obj.remove(vo, responder, fault);
					}else{
						obj.remove(obj.removemethod, vo, responder, obj.removeobj, fault);
					}
				}
			}
		}
		private function getMethodCombination(main:String, link:String):Object{
			if(_linkCombination[String(main+"_"+link).toLowerCase()]){
				return _linkCombination[String(main+"_"+link).toLowerCase()];
			}
			return null;
		}
		private function getClassType(vo:CoreVO):String{
			return getQualifiedClassName(vo).split("::")[1];
		}
		public function deleteLink(main:CoreVO, link:CoreVO, responder:Function, fault:Function = null):void{
			var xm:XML = new XML("<inl_request/>");
			var obj:Object = getMethodCombination(getClassType(main), getClassType(link));
			if(obj){ 
				xm.@method = obj.remove; 
				xm.mid = new XML("<mid>"+main[main.idField]+"</mid>");
				xm.lid = new XML("<lid>"+link[link.idField]+"</lid>");
				process(responder, xm, null, fault);
			}
			
		}
	}
}
internal class SingletonEnforcer {} 